Hi! Ich bin Dario Corno, auch als rIo von SpinningKids bekannt.. Als erstes möchte ich erklären, warum ich mich entschieden habe, dieses Tutorial zu schreiben. Ich bin ein Scener seit 1989. Ich möchte, dass Sie alle einige Demos herunterladen, so dass Sie verstehen, was ein Demo ist und was Demo-Effekte sind.
Demos werden gemacht, um Hardcore und manchmal 'brutales' Programmieren sowie künstlerische Fähigkeiten zu zeigen. Sie können in der Regel in heutigen Demos einige echte Killer-Effekte finden! Dies hier wird kein Killer-Effekt Tutorial, aber das Endresultat ist sehr cool! Sie finden eine große Ansammlung an Demos unter http://www.pouet.net und http://ftp.scene.org.
Nun, da wir die Einleitung hinter uns haben, können wir mit dem Tutorial weitermachen...
Ich werde erklären, wie man einen Augenschmauseffekt (der in Demos benutzt wird) macht, der aussieht wie strahlenförmige Verschwommenheit (Radial Bluer). Manchmal wird auch von volumetrischen Licht gesprochen, glaube Sie das nicht, es ist nur ein gefakter Radial Blur! ;D
Radial Blur wurde normalerweise realsiert (als es nur Software Renderer gab) indem jeder Pixel des original Bildes in eine entgegengesetzte Richtung vom Zentrum des Blurs aus verwischt wurde.
Mit heutiger Hardware ist es ziemlich schwierig von Hand aus zu verwischen und den selben Farb-Buffer zu verwenden (zumindest auf eine Art, die von allen Grafikkarten unterstütz wird), weshalb wir einen kleinen Trick anwenden müssen, um den selben Effekt zu erzielen.
Als kleiner Bonus lernen Sie neben dem Radial Blur Effekt auch, wie Sie auf einfache Art auf eine Textur rendern!
Ich habe mich entschlossen eine Feder als Form in diesem Tutorial zu verwenden, da es eine coole Form ist und ich die Würfel satt habe :)
Es ist wichtig anzumerken, dass dieses Tutorial mehr ein Leitfaden ist, wie man den Effekt erzeugt. Ich werde nicht großartig ins Detail gehen, wenn ich den Code erkläre. Das meiste sollten Sie bereits im Schlaf kennen :)
Folgend sind die Variablen Definitionen und die verwendeten Includes:
#include < math.h> // wir benötigen etwas Mathe float angle; // wird benutzt um die Schneckenlinien zu rotieren float vertexes[4][3]; // enthält Fließkommawerte Infos für 4 Sätze an Vertices float normal[3]; // Ein Array um die Normalenvektor Daten zu speichern GLuint BlurTexture; // Ein Unsigned Int um die Texturnummer zu speichern
GLuint EmptyTexture() // erzeuge eine leere Textur
{
GLuint txtnumber; // Textur ID
unsigned int* data; // gespeicherte Daten
// erzeuge Speicherplatz für Texturdaten (128x128x4)
data = (unsigned int*)new GLuint[((128 * 128)* 4 * sizeof(unsigned int))];
ZeroMemory(data,((128 * 128)* 4 * sizeof(unsigned int))); // lösche Speicherplatz glGenTextures(1, &txtnumber); // erzeuge 1 Textur glBindTexture(GL_TEXTURE_2D, txtnumber); // Binde die Textur glTexImage2D(GL_TEXTURE_2D, 0, 4, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // Builde Textur und benutze dieInformation in data glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); delete [] data; // gebe data frei return txtnumber; // gebe die Textur ID zurück }
void ReduceToUnit(float vector[3]) // Reduziert einen Normalenvektor (3 Koordinaten)
{ // in einen Einheiten Normalenvektor mit einer Länge von eins.
float length; // enthält die Längeneinheit
// berechnet die Länge des Vektors
length = (float)sqrt((vector[0]*vector[0]) + (vector[1]*vector[1]) + (vector[2]*vector[2]));
if(length == 0.0f) // verhindert einen Division durch 0 Fehler indem ein akzeptabler
length = 1.0f; // Wert für Vektoren die fast 0 sind zur Verfügung gestellt wird.
vector[0] /= length; // dividiere jedes Element durch
vector[1] /= length; // die Länge, daraus resultiert ein
vector[2] /= length; // Einheiten Normalenvektor.
}
void calcNormal(float v[3][3], float out[3]) // berechnet Normalenvektor für ein Quad indem 3 Punkte verwendet werden
{
float v1[3],v2[3]; // Vektor 1 (x,y,z) & Vektor 2 (x,y,z)
static const int x = 0; // Definiere X Koordinate
static const int y = 1; // Definiere Y Koordinate
static const int z = 2; // Definiere Z Koordinate
// Findet den Vektor zwischen 2 Punkten durch Subtraktion
// der x,y,z Koordinaten von einem Punkt zum anderen.
// berechen den Vektor von Punkt 1 zu Punkt 0
v1[x] = v[0][x] - v[1][x]; // Vector 1.x=Vertex[0].x-Vertex[1].x
v1[y] = v[0][y] - v[1][y]; // Vector 1.y=Vertex[0].y-Vertex[1].y
v1[z] = v[0][z] - v[1][z]; // Vector 1.z=Vertex[0].y-Vertex[1].z
// berechen den Vektor von Punkt 2 zu Punkt 1
v2[x] = v[1][x] - v[2][x]; // Vector 2.x=Vertex[0].x-Vertex[1].x
v2[y] = v[1][y] - v[2][y]; // Vector 2.y=Vertex[0].y-Vertex[1].y
v2[z] = v[1][z] - v[2][z]; // Vector 2.z=Vertex[0].z-Vertex[1].z
// berechne das Kreuzprodukt um einen Oberflächen Normalenvektor zu erhalten
out[x] = v1[y]*v2[z] - v1[z]*v2[y]; // Kreuzprodukt für Y - Z
out[y] = v1[z]*v2[x] - v1[x]*v2[z]; // Kreuzprodukt für X - Z
out[z] = v1[x]*v2[y] - v1[y]*v2[x]; // Kreuzprodukt für X - Y
ReduceToUnit(out); // Normalisiere den Vektor
}
void ProcessHelix() // zeichnet eine Schneckenform (Helix)
{
GLfloat x; // Helix x Koordinate
GLfloat y; // Helix y Koordinate
GLfloat z; // Helix z Koordinate
GLfloat phi; // Winkel
GLfloat theta; // Winkel
GLfloat v,u; // Winkel
GLfloat r; // Radius der Windung
int twists = 5; // 5 Windungen
GLfloat glfMaterialColor[]={0.4f,0.2f,0.8f,1.0f}; // Setzt die Material Farbe
GLfloat specular[]={1.0f,1.0f,1.0f,1.0f}; // Setzt spekuläre Beleuchtung
glLoadIdentity(); // Resette die Modelview Matrix
gluLookAt(0, 5, 50, 0, 0, 0, 0, 1, 0); // Augen Position (0,5,50) Zentrum der Szene (0,0,0)
// Up auf der Y Achse.
glPushMatrix(); // Pushe die Modelview Matrix
glTranslatef(0,0,-50); // Translatiere 50 Einheiten in den Screen hinein
glRotatef(angle/2.0f,1,0,0); // Rotiere um angle/2 auf der X-Achse
glRotatef(angle/3.0f,0,1,0); // Rotatiere um angle/3 auf der Y-Achse
glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,glfMaterialColor);
glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specular);
r=1.5f; // Radius
glBegin(GL_QUADS); // fange an Quads zu zeichnen
for(phi=0; phi <= 360; phi+=20.0) // 360 Grad in 20er Schritten
{
for(theta=0; theta<=360*twists; theta+=20.0) // 360 Grad * Anzahl der Windungen in 20er Schritten
{
v=(phi/180.0f*3.142f); // berechne Winkel des ersten Punktes ( 0 )
u=(theta/180.0f*3.142f); // berechne Winkel des ersten Punktes ( 0 )
x=float(cos(u)*(2.0f+cos(v) ))*r; // berechne x Position (erster Punkt)
y=float(sin(u)*(2.0f+cos(v) ))*r; // berechne y Position (erster Punkt)
z=float((( u-(2.0f*3.142f)) + sin(v) ) * r); // berechne z Position (erster Punkt)
vertexes[0][0]=x; // Setze x Wert des erstenVertex
vertexes[0][1]=y; // Setze y Wert des erstenVertex
vertexes[0][2]=z; // Setze z Wert des erstenVertex
v=(phi/180.0f*3.142f); // berechne Winkel des zweiten Punktes ( 0 )
u=((theta+20)/180.0f*3.142f); // berechne Winkel des zweiten Punktes ( 20 )
x=float(cos(u)*(2.0f+cos(v) ))*r; // berechne x Position (zweiter Punkt)
y=float(sin(u)*(2.0f+cos(v) ))*r; // berechne y Position (zweiter Punkt)
z=float((( u-(2.0f*3.142f)) + sin(v) ) * r); // berechne z Position (zweiter Punkt)
vertexes[1][0]=x; // Setze x Wert des zweitenVertex
vertexes[1][1]=y; // Setze y Wert des zweitenVertex
vertexes[1][2]=z; // Setze z Wert des zweitenVertex
v=((phi+20)/180.0f*3.142f); // berechne Winkel des dritten Punktes ( 20 )
u=((theta+20)/180.0f*3.142f); // berechne Winkel des dritten Punktes ( 20 )
x=float(cos(u)*(2.0f+cos(v) ))*r; // berechne x Position (dritter Punkt)
y=float(sin(u)*(2.0f+cos(v) ))*r; // berechne y Position (dritter Punkt)
z=float((( u-(2.0f*3.142f)) + sin(v) ) * r); // berechne z Position (dritter Punkt)
vertexes[2][0]=x; // Setze x Wert des drittenVertex
vertexes[2][1]=y; // Setze y Wert des drittenVertex
vertexes[2][2]=z; // Setze z Wert des drittenVertex
v=((phi+20)/180.0f*3.142f); // berechne Winkel des vierten Punktes ( 20 )
u=((theta)/180.0f*3.142f); // berechne Winkel des vierten Punktes ( 0 )
x=float(cos(u)*(2.0f+cos(v) ))*r; // berechne x Position (vierter Punkt)
y=float(sin(u)*(2.0f+cos(v) ))*r; // berechne y Position (vierter Punkt)
z=float((( u-(2.0f*3.142f)) + sin(v) ) * r); // berechne z Position (vierter Punkt)
vertexes[3][0]=x; // Setze x Wert des viertenVertex
vertexes[3][1]=y; // Setze y Wert des viertenVertex
vertexes[3][2]=z; // Setze z Wert des viertenVertex
calcNormal(vertexes,normal); // berechne den Quad Normelnvektor
glNormal3f(normal[0],normal[1],normal[2]); // Setze den Normalenvektor
// Render den Quad
glVertex3f(vertexes[0][0],vertexes[0][1],vertexes[0][2]);
glVertex3f(vertexes[1][0],vertexes[1][1],vertexes[1][2]);
glVertex3f(vertexes[2][0],vertexes[2][1],vertexes[2][2]);
glVertex3f(vertexes[3][0],vertexes[3][1],vertexes[3][2]);
}
}
glEnd(); // fertig mit dem rendern von Quads
glPopMatrix(); // Poppe die Matrix
}
void ViewOrtho() // Setzt eine Ortho View
{
glMatrixMode(GL_PROJECTION); // wähle Projection
glPushMatrix(); // Pushe die Matrix
glLoadIdentity(); // Resette die Matrix
glOrtho( 0, 640 , 480 , 0, -1, 1 ); // wähle Ortho Modus (640x480)
glMatrixMode(GL_MODELVIEW); // wähle Modelview Matrix
glPushMatrix(); // Pushe die Matrix
glLoadIdentity(); // Resette die Matrix
}
void ViewPerspective() // Setze eine Perspective View
{
glMatrixMode( GL_PROJECTION ); // wähle Projection
glPopMatrix(); // Poppe die Matrix
glMatrixMode( GL_MODELVIEW ); // wähle Modelview
glPopMatrix(); // Poppe die Matrix
}
void RenderToTexture() // Rendert auf eine Textur
{
glViewport(0,0,128,128); // Setze unseren Viewport (passend zur Texturgröße)
ProcessHelix(); // Render Helix
glBindTexture(GL_TEXTURE_2D,BlurTexture); // Binde die Blur Textur
// kopiere unseren ViewPort auf die Blur Textur (von 0,0 bis 128,128... kein Rahmen)
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 0, 0, 128, 128, 0);
glClearColor(0.0f, 0.0f, 0.5f, 0.5); // Setze die Löschfarbe auf ein mittleres Blau
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // lösche den Screen und Depth Buffer
glViewport(0 , 0,640 ,480); // Setze Viewport (0,0 bis 640x480)
}
void DrawBlur(int times, float inc) // zeichne das verwischte Bild
{
float spost = 0.0f; // Startende Texturkoordinaten Offset
float alphainc = 0.9f / times; // Fade Geschwindigkeit für Alpha Blending
float alpha = 0.2f; // Start Alpha Wert
// deaktiviere AutoTexture Koordinaten
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_2D); // aktiviere 2D Texture Mapping
glDisable(GL_DEPTH_TEST); // deaktiviere Depth Testing
glBlendFunc(GL_SRC_ALPHA,GL_ONE); // Setze Blending Mode
glEnable(GL_BLEND); // aktiviere Blending
glBindTexture(GL_TEXTURE_2D,BlurTexture); // Binde die Blur Textur
ViewOrtho(); // wechsel zur Ortho View
alphainc = alpha / times; // alphainc=0.2f / Wie of Blur gerendert werden soll
glBegin(GL_QUADS); // fange an Quads zu zeichnen
for (int num = 0;num < times;num++) // Wie oft Blur gerendert werden soll
{
glColor4f(1.0f, 1.0f, 1.0f, alpha); // Setze den Alpha Wert (beginnend bei 0.2)
glTexCoord2f(0+spost,1-spost); // Texturkoordinate ( 0, 1 )
glVertex2f(0,0); // erster Vertex ( 0, 0 )
glTexCoord2f(0+spost,0+spost); // Texturkoordinate ( 0, 0 )
glVertex2f(0,480); // zweiter Vertex ( 0, 480 )
glTexCoord2f(1-spost,0+spost); // Texturkoordinate ( 1, 0 )
glVertex2f(640,480); // dritter Vertex ( 640, 480 )
glTexCoord2f(1-spost,1-spost); // Texturkoordinate ( 1, 1 )
glVertex2f(640,0); // vierter Vertex ( 640, 0 )
spost += inc; // inkrementiere stufenweise spost (zoomt näher ans Textur Zentrum)
alpha = alpha - alphainc; // dekrementiere stufenweise alpha (schrittweises ausblenden des Bildes)
}
glEnd(); // fertig mit dem Zeichnen der Quads
ViewPerspective(); // wechsel zur perspektivischen View
glEnable(GL_DEPTH_TEST); // aktiviere Depth Testing
glDisable(GL_TEXTURE_2D); // deaktiviere 2D Texture Mapping
glDisable(GL_BLEND); // deaktiviere Blending
glBindTexture(GL_TEXTURE_2D,0); // Unbind die Blur Textur
}
void Draw (void) // Zeichne die Szene
{
glClearColor(0.0f, 0.0f, 0.0f, 0.5); // Setze die Farbe auf schwarz
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Löscht den Bildschirm und den Depth-Buffer
glLoadIdentity(); // Resette die View
RenderToTexture(); // Render auf eine Textur
ProcessHelix(); // zeichne unsere Schneckenform
DrawBlur(25,0.02f); // zeichne den Blur Effect
glFlush (); // Flushe die GL Rendering Pipeline
}